/* eslint-disable no-param-reassign */
import { recursiveIterate } from '@ibiz-template/core';
import {
  MDControlController,
  ITreeEvent,
  ITreeController,
  ITreeState,
  ITreeNodeData,
  MDCtrlLoadParams,
  UIActionUtil,
  UIActionButtonState,
  ButtonContainerState,
  TreeNodeData,
} from '@ibiz-template/runtime';
import {
  IDEContextMenu,
  IDETBUIActionItem,
  IDEToolbarItem,
  IDETree,
  IDETreeNode,
} from '@ibiz/model-core';
import { TreeService } from './tree.service';

/**
 * 树部件控制器
 * @author lxm
 * @date 2023-05-26 08:20:46
 * @export
 * @class TreeController
 * @extends {MDControlController<IDETree, ITreeState, ITreeEvent>}
 * @implements {ITreeController}
 */
export class TreeController
  extends MDControlController<IDETree, ITreeState, ITreeEvent>
  implements ITreeController
{
  declare service: TreeService;

  protected initState(): void {
    super.initState();
    // 根节点初始化
    this.state.defaultExpandedKeys = [];
    this.state.size = 0;
    this.state.contextMenus = {};
  }

  protected async onCreated(): Promise<void> {
    await super.onCreated();
    this.service = new TreeService(this.model);
    await this.service.init(this.context);

    // 初始化上下文菜单状态
    this.model.detreeNodes!.forEach(node => {
      if (node.decontextMenu?.detoolbarItems?.length) {
        this.initContextMenuState(node.decontextMenu);
      }
    });
  }

  /**
   * 树部件加载，从根节点开始重新加载
   *
   * @author lxm
   * @date 2022-08-19 14:08:50
   */
  async load(args: MDCtrlLoadParams = {}): Promise<ITreeNodeData[]> {
    const isInitialLoad = args.isInitialLoad === true;
    const nodes = await this.loadNodes();
    this.state.expandedKeys = this.calcExpandedKeys(nodes);
    await this.afterLoad(args, nodes);
    this.state.isLoaded = true;
    await this.evt.emit('onLoadSuccess', {
      isInitialLoad,
    });

    return nodes;
  }

  /**
   * 加载子节点数据
   *
   * @param {(ITreeNodeData | undefined)} parentNode
   * @returns {*}
   * @memberof TreeController
   */
  async loadNodes(parentNode?: ITreeNodeData): Promise<ITreeNodeData[]> {
    const params = await this.getFetchParams();
    const hasQuery = !!params.query;
    const defaultExpandedKeys = !parentNode
      ? this.state.defaultExpandedKeys
      : undefined;

    // 请求服务获取子节点数据
    await this.startLoading();
    let nodes;
    try {
      nodes =
        (await this.service.fetchChildNodes(parentNode, {
          context: this.context.clone(),
          params,
          hasQuery,
          defaultExpandedKeys,
        })) || [];
    } finally {
      await this.endLoading();
    }

    // 有父节点绑定到父节点数据上，无父节点替换rootNodes
    if (parentNode) {
      parentNode.children = nodes;
    } else {
      this.state.rootNodes = nodes;
    }

    // 更新items
    this.state.items = [];
    recursiveIterate(
      { children: this.state.rootNodes },
      (node: ITreeNodeData) => {
        this.state.items.push(node);
      },
    );

    return nodes;
  }

  /**
   * 树节点点击事件
   *
   * @param {ITreeNodeData} nodeData
   * @returns {*}  {Promise<void>}
   * @memberof TreeController
   */
  async onTreeNodeClick(nodeData: ITreeNodeData): Promise<void> {
    // 单选时，单击才会触发选中逻辑
    if (this.state.singleSelect) {
      // 选中相关处理
      const { selectedData } = this.state;
      // 选中里没有则添加，有则删除
      const filterArr = selectedData.filter(item => item.id !== nodeData.id);
      if (filterArr.length === selectedData.length) {
        this.setSelection(
          this.state.singleSelect
            ? [nodeData]
            : selectedData.concat([nodeData]),
        );
      } else {
        this.setSelection(filterArr);
      }
    }

    // 激活事件
    if (this.state.mdctrlActiveMode === 1) {
      await this.setActive(nodeData);
    }
  }

  /**
   * 树节点双击事件
   * @author lxm
   * @date 2023-05-29 10:01:36
   * @param {ITreeNodeData} nodeData
   * @return {*}  {Promise<void>}
   */
  async onDbTreeNodeClick(nodeData: ITreeNodeData): Promise<void> {
    if (this.state.mdctrlActiveMode === 2) {
      await this.setActive(nodeData);
    }
  }

  setActive(item: TreeNodeData): Promise<void> {
    const nodeParams = this.parseTreeNodeData(item);
    return this.evt.emit('onActive', { ...nodeParams, nodeData: item });
  }

  setSelection(selection: IData[]): void {
    // todo 当自己点选中时，父节点选不选中，如果选中需要在这边优化
    super.setSelection(selection);
  }

  /**
   * 获取节点模型
   * @author lxm
   * @date 2023-07-27 10:47:58
   * @param {string} id
   * @return {*}  {(IDETreeNode | undefined)}
   */
  getNodeModel(id: string): IDETreeNode | undefined {
    return this.model.detreeNodes?.find(item => item.id === id);
  }

  /**
   * 执行界面行为
   * @author lxm
   * @date 2023-07-27 05:31:36
   * @param {string} uiActionId
   * @param {ITreeNodeData} nodeData
   * @param {MouseEvent} event
   * @return {*}  {Promise<void>}
   */
  async doUIAction(
    uiActionId: string,
    nodeData: ITreeNodeData,
    event: MouseEvent,
  ): Promise<void> {
    const eventArgs = this.getEventArgs();
    const nodeParams = this.parseTreeNodeData(nodeData);
    await UIActionUtil.execAndResolved(uiActionId!, {
      ...eventArgs,
      ...nodeParams,
      event,
    });
  }

  /**
   * 初始化上下文菜单状态
   * @author lxm
   * @date 2023-08-01 04:53:32
   * @protected
   * @param {IDEContextMenu} contextMenu
   */
  protected initContextMenuState(contextMenu: IDEContextMenu): void {
    if (this.state.contextMenus[contextMenu.id!]) {
      // 已创建过得就不重复创建了。
      return;
    }
    const menuState = new ButtonContainerState();
    // 初始化工具栏状态控制对象
    recursiveIterate(
      contextMenu,
      (item: IDEToolbarItem) => {
        if (item.itemType === 'DEUIACTION') {
          const uiItem = item as IDETBUIActionItem;
          const buttonState = new UIActionButtonState(
            uiItem.id!,
            this.context.srfappid!,
            uiItem.uiactionId!,
          );

          menuState.addState(uiItem.id!, buttonState);
        }
      },
      { childrenFields: ['detoolbarItems'] },
    );
    this.state.contextMenus[contextMenu.id!] = menuState;
    this.state.contextMenus[contextMenu.id!].update();
  }

  /**
   * 解析树节点获取通用数据，和完整的上下文和视图参数。
   * @author lxm
   * @date 2023-08-09 11:45:34
   * @protected
   * @param {TreeNodeData} nodeData
   */
  protected parseTreeNodeData(nodeData: TreeNodeData): {
    data: IData[];
    context: IContext;
    params: IParams;
  } {
    return {
      data: [{ ...nodeData, ...(nodeData.deData || {}) }],
      context: Object.assign(this.context.clone(), nodeData.context || {}),
      params: { ...this.params, ...(nodeData.params || {}) },
    };
  }

  /**
   * 计算展开节点集合
   * @author lxm
   * @date 2023-08-09 05:19:36
   * @param {ITreeNodeData[]} nodes
   * @param {boolean} [isRoot=false]
   * @return {*}  {string[]}
   */
  calcExpandedKeys(nodes: ITreeNodeData[]): string[] {
    let expandedKeys: string[] = [];
    recursiveIterate({ children: nodes }, (node: ITreeNodeData) => {
      if (node.children?.length) {
        expandedKeys.push(node.id);
      }
    });
    if (this.state.defaultExpandedKeys.length) {
      expandedKeys.push(...this.state.defaultExpandedKeys);
      expandedKeys = Array.from(new Set(expandedKeys));
    }
    return expandedKeys;
  }
}
